import { Indent, Highlight, HighlightGreen } from './../../core/constants.js';
import fs from 'node:fs/promises';
import path from 'node:path';
import { color } from '../../core/color.js';
import { loadConfig } from '../../core/config.js';
import { logger } from '../../core/logger.js';
import { Command } from '../utils/cli-handler.js';
import { createRequire } from 'node:module';
import { exec, PackageDir } from '../utils/utils.js';
import { getPackageManager, getPackageExecutor } from '../utils/runtime-utils.js';
import { Compiler } from '../utils/compiler.js';
import { Spinner } from '../utils/spinner.js';
import readline from 'node:readline';

const require2 = createRequire(import.meta.url);
const localPrefixes = ["file:", ".", "/", "~", ":"];
const command = new Command("add").description("Adds a plugin to your Robo.").option("-f", "--force", "forcefully install & register packages").option("-ns", "--no-seed", "skip the seeding of files from the plugin").option("-s", "--silent", "do not print anything").option("-v", "--verbose", "print more information for debugging").option("-y", "--yes", "auto-accept seed files").positionalArgs(true).handler(addAction);
var add_default = command;
async function addAction(packages, options) {
  logger({
    enabled: !options.silent,
    level: options.verbose ? "debug" : "info"
  });
  logger.debug(`Adding plugins:`, packages);
  logger.debug(`Current working directory:`, process.cwd());
  const startTime = Date.now();
  const seed = !options["no-seed"];
  const s = packages.length > 1 ? "s" : "";
  if (packages.length === 0) {
    logger.error(`No packages specified. Use ${color.bold("robo add <package>")} to add a plugin.`);
    return;
  }
  const spinner = new Spinner();
  logger.log("\n" + Indent, color.bold(`\u{1F4E6} Installing plugin${s}`));
  spinner.setText(packages.map((pkg) => `${Indent}    - {{spinner}} ${Highlight(pkg)}`).join("\n") + `

`);
  spinner.start();
  if (options.verbose) {
    spinner.stop(false, false);
  }
  const config = await loadConfig("robo", true);
  const nameMap = {};
  const pendingRegistration = await Promise.all(
    packages.filter((pkg) => {
      return options.force || !config.plugins?.includes(pkg);
    }).map(async (pkg) => {
      const isLocal = localPrefixes.some((prefix) => {
        return prefix === ":" ? pkg.indexOf(prefix) === 1 : pkg.startsWith(prefix);
      });
      if (isLocal) {
        const packageJsonPath2 = path.join(pkg, "package.json");
        const packageJson2 = JSON.parse(await fs.readFile(packageJsonPath2, "utf-8"));
        nameMap[pkg] = packageJson2.name;
        return packageJson2.name;
      }
      nameMap[pkg] = pkg;
      return pkg;
    })
  );
  spinner.setText(pendingRegistration.map((pkg) => `${Indent}    - {{spinner}} ${Highlight(pkg)}`).join("\n") + `

`);
  logger.debug("Pending registration add:", pendingRegistration);
  const packageJsonPath = path.join(process.cwd(), "package.json");
  const packageJson = require2(packageJsonPath);
  const pendingInstall = packages.filter((pkg) => {
    return options.force || !Object.keys(packageJson.dependencies ?? {})?.includes(pkg) && !config.plugins?.find((p) => Array.isArray(p) && p[0] === pkg);
  });
  logger.debug(`Pending installation add:`, pendingInstall);
  if (pendingInstall.length > 0) {
    const packageManager = getPackageManager();
    const command2 = packageManager === "npm" ? "install" : "add";
    logger.debug(`Using package manager:`, packageManager);
    try {
      await exec([packageManager, command2, ...pendingInstall], {
        stdio: options.force ? "inherit" : "ignore"
      });
      logger.debug(`Successfully installed packages!`);
    } catch (error) {
      logger.error(`Failed to install packages:`, error);
      if (!options.force) {
        return;
      }
    }
  }
  await Promise.all(pendingRegistration.map((pkg) => createPluginConfig(pkg, {})));
  spinner.setText(
    pendingRegistration.map((pkg) => `${Indent}    ${HighlightGreen("\u2714 " + pkg)}  `).join("\n") + `

`,
    false
  );
  spinner.stop(false, false);
  const pluginsWithSeeds = packages.filter((pkg) => Compiler.hasSeed(pkg));
  logger.debug(`Plugins with seeds:`, pluginsWithSeeds);
  if (seed && options.sync && pluginsWithSeeds.length > 0) {
    const executor = getPackageExecutor(false);
    const command2 = executor + " robo add " + packages.join(" ");
    logger.log(Indent, color.bold(`\u{1F331} Seed files detected`));
    logger.log(Indent, "   Run the following to copy seed files:", "\n   " + Indent, Highlight(command2));
    logger.log("");
  } else if (seed && pluginsWithSeeds.length > 0) {
    const pluginSeeds = await Promise.all(
      packages.map(async (pkg) => {
        const manifest = await Compiler.useManifest({
          basePath: path.resolve(PackageDir, "..", pkg)
        });
        const description = manifest.__robo?.seed?.description;
        return `${Indent}    - ${Highlight(nameMap[pkg])}${description ? ": " + description : ""}`;
      })
    );
    logger.log(Indent, color.bold(`\u{1F331} Seed files detected`));
    logger.log(pluginSeeds.join("\n"), "\n");
    const promptUser = (question) => {
      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
      });
      return new Promise((resolve) => {
        rl.question(question, (input) => {
          rl.close();
          resolve(input);
        });
      });
    };
    let seedConsent = options.yes;
    if (!seedConsent) {
      const response = await promptUser(Indent + `    Would you like to include these files? ${color.dim("[Y/n]")}: `);
      seedConsent = response.toLowerCase().trim() === "y";
      logger.log("");
    }
    if (seedConsent) {
      await Promise.all(
        packages.map(async (pkg) => {
          try {
            await Compiler.useSeed(pkg);
          } catch (error) {
            logger.error(`Failed to copy seed files for plugin ${color.bold(pkg)}:`, error);
          }
        })
      );
    }
  }
  logger.log(Indent, `\u2728 Plugin${s} successfully installed and ready to use.
`);
  logger.debug(`Finished in ${Date.now() - startTime}ms`);
}
async function createPluginConfig(pluginName, config) {
  const pluginParts = pluginName.replace(/^@/, "").split("/");
  await fs.mkdir(path.join(process.cwd(), "config", "plugins"), {
    recursive: true
  });
  if (pluginName.startsWith("@")) {
    await fs.mkdir(path.join(process.cwd(), "config", "plugins", pluginParts[0]), {
      recursive: true
    });
  }
  const pluginPath = path.join(process.cwd(), "config", "plugins", ...pluginParts) + (Compiler.isTypescriptProject() ? ".ts" : ".mjs");
  const pluginConfig = JSON.stringify(config) + "\n";
  logger.debug(`Writing ${pluginName} config to ${pluginPath}...`);
  await fs.writeFile(pluginPath, `export default ${pluginConfig}`);
}

export { addAction, add_default as default };
